import json
import asyncio
import time
import threading
import yaml
import logging
import sys
from typing import Dict, Any, List, Optional
import win32file  # 需安装 pywin32: pip install pywin32

from util.tasker import Tasker
from util.serials import AsyncSerialDevice
from util.commander import Commander

# 配置基础日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - Modbus - %(levelname)s - %(message)s')
logger = logging.getLogger("ModbusClient")


class PipeCommunication:
    """管道通信模块，负责与C++端的命名管道交互"""
    def __init__(self, pipe_name=r'\\.\pipe\sensor_server_pipe'):
        self.pipe_name = pipe_name
        self.pipe_handle = None
        self.running = False
        self.listener_thread = None
        self.modbus_client = None  # 引用ModbusClient实例

    def set_modbus_client(self, client):
        """关联ModbusClient实例，用于处理业务逻辑"""
        self.modbus_client = client

    def connect(self):
        """连接到C++端的命名管道"""
        try:
            self.pipe_handle = win32file.CreateFile(
                self.pipe_name,
                win32file.GENERIC_READ | win32file.GENERIC_WRITE,
                0,  # 不共享
                None,  # 默认安全属性
                win32file.OPEN_EXISTING,  # 打开已存在的管道
                0,
                None
            )
            logger.info("✅ 已连接到C++管道")
            self.running = True
            self.listener_thread = threading.Thread(target=self._listen, daemon=True)
            self.listener_thread.start()
            return True
        except Exception as e:
            logger.error(f"❌ 管道连接失败: {str(e)}")
            return False

    def send(self, data: Dict[str, Any]):
        """向C++端发送消息"""
        if not self.pipe_handle or not self.running:
            logger.warning("⚠️ 管道未连接，发送失败")
            return False
        try:
            # 消息格式：JSON字符串 + 换行符（作为分隔符）
            msg = json.dumps(data) + "\r\n"
            win32file.WriteFile(self.pipe_handle, msg.encode('utf-8'))
            logger.info(f"📤 发送到C++: {data}")
            return True
        except Exception as e:
            logger.error(f"❌ 管道发送失败: {str(e)}")
            return False

    def _listen(self):
        """监听C++端发送的消息（独立线程）"""
        while self.running:
            try:
                # 读取管道数据（最大4096字节）
                _, data = win32file.ReadFile(self.pipe_handle, 4096)
                if data:
                    msg_str = data.decode('utf-8').strip()
                    logger.info(f"📥 收到C++消息: {msg_str}")
                    self._handle_message(msg_str)
            except Exception as e:
                if "断开的管道" in str(e):
                    logger.warning("⚠️ 管道已断开连接")
                    self.running = False
                    break
                logger.error(f"❌ 管道读取失败: {str(e)}")
            time.sleep(0.05)  # 降低CPU占用

    def _handle_message(self, msg_str: str):
        """处理C++端消息，调用ModbusClient对应方法"""
        if not self.modbus_client:
            logger.warning("⚠️ 未关联ModbusClient，无法处理消息")
            return

        try:
            msg = json.loads(msg_str)
            msg_type = msg.get("type")

            # 处理设备控制指令
            if msg_type == "control":
                self._handle_control_command(msg)
            # 处理状态查询指令
            elif msg_type == "query":
                self._handle_query_command(msg)
            # 处理退出指令
            elif msg_type == "exit":
                logger.info(f"收到退出指令: {msg.get('msg')}")
                self.running = False
                self.modbus_client.disconnect()
                sys.exit(0)
            else:
                logger.warning(f"⚠️ 未知消息类型: {msg_type}")
                self.send({"type": "error", "msg": f"未知消息类型: {msg_type}"})

        except json.JSONDecodeError:
            logger.error(f"❌ 消息解析失败（非JSON格式）: {msg_str}")
            self.send({"type": "error", "msg": "无效的JSON格式"})
        except Exception as e:
            logger.error(f"❌ 消息处理异常: {str(e)}", exc_info=True)
            self.send({"type": "error", "msg": str(e)})

    def _handle_control_command(self, msg: Dict):
        """处理控制指令（如启动/停止设备）"""
        control_type = msg.get("control_type")
        data = msg.get("data", {})
        
        if not control_type or not data:
            self.send({"type": "error", "msg": "控制指令缺少参数"})
            return

        # 调用ModbusClient的指令处理方法
        try:
            self.modbus_client._process_single_command({
                "control_type": control_type,
                "data": data
            })
            self.send({
                "type": "control_result",
                "status": "success",
                "msg": f"已执行{control_type}指令: {data}"
            })
        except Exception as e:
            self.send({
                "type": "control_result",
                "status": "failed",
                "msg": str(e)
            })

    def _handle_query_command(self, msg: Dict):
        """处理状态查询指令"""
        query_type = msg.get("query_type")
        name = msg.get("name")
        action = msg.get("action")

        if not query_type or not name or not action:
            self.send({"type": "error", "msg": "查询指令缺少参数"})
            return

        # 调用ModbusClient的状态查询方法
        try:
            # 根据查询类型获取对应的消息字典
            if query_type == "power":
                msg_dict = self.modbus_client.commander.power_message_dict
            elif query_type == "key":
                msg_dict = self.modbus_client.commander.key_message_dict
            elif query_type == "discrete":
                msg_dict = self.modbus_client.commander.discrete_message_dict
            else:
                raise ValueError(f"不支持的查询类型: {query_type}")

            # 提交查询任务并等待结果（通过回调处理）
            def on_query_done(result):
                self.send({
                    "type": "query_result",
                    "query_type": query_type,
                    "data": result
                })

            # 使用tasker的回调机制处理异步查询结果
            task = self.modbus_client._send_message_async(msg_dict, name, action)
            self.modbus_client.tasker.add_task_with_callback(task, on_query_done)

        except Exception as e:
            self.send({
                "type": "query_result",
                "status": "failed",
                "msg": str(e)
            })

    def close(self):
        """关闭管道连接"""
        self.running = False
        if self.listener_thread and self.listener_thread.is_alive():
            self.listener_thread.join(timeout=1.0)
        if self.pipe_handle:
            try:
                win32file.CloseHandle(self.pipe_handle)
            except Exception as e:
                logger.warning(f"⚠️ 关闭管道失败: {str(e)}")
        logger.info("🔚 管道通信已关闭")


class ModbusClient:
    """Modbus设备客户端，整合管道通信与原有设备控制逻辑"""
    def __init__(self,
                 serials_config_path="./configs/parameters.yaml",
                 code_config_path="./configs/settings.yaml",
                 save_config_path="./configs/saving.yaml",
                 ):
        # 设备控制相关属性
        self.tasker = None
        self.serial = None
        self.save_dir = None
        self.connected = False  # 标记本地设备初始化状态
        self.serial_config_path = serials_config_path
        self.save_config_path = save_config_path
        self.code_config_path = code_config_path
        self.commander = None

        # 管道通信模块
        self.pipe = PipeCommunication()
        self.pipe.set_modbus_client(self)  # 关联自身到管道模块

    def _load_serial_config(self) -> Dict:
        """加载Serial配置"""
        try:
            with open(self.serial_config_path, "r", encoding="utf-8") as f:
                config = yaml.safe_load(f)
            logger.info(f"✅ 成功加载Serial配置: {self.serial_config_path}")
            return config
        except Exception as e:
            logger.error(f"❌ Serial配置加载失败: {str(e)}", exc_info=True)
            raise

    def _load_save_config(self) -> Dict:
        """加载存储路径配置"""
        try:
            with open(self.save_config_path, "r", encoding="utf-8") as f:
                config = yaml.safe_load(f)
            save_array = config.get("save", [])
            if not isinstance(save_array, list) or len(save_array) == 0:
                raise ValueError(f"配置文件 {self.save_config_path} 中 'save' 必须是非空数组")
            self.save_dir = "/".join(save_array)
            logger.info(f"✅ 成功加载并拼接存储路径: {self.save_dir}（源配置：{save_array}）")
            return config
        except Exception as e:
            logger.error(f"❌ 存储路径配置加载失败: {str(e)}", exc_info=True)
            raise

    def connect(self):
        """初始化设备并连接管道"""
        self.connected = False
        try:
            # 1. 初始化设备控制模块
            self.tasker = Tasker()
            logger.info("✅ Tasker已启动")

            # 2. 初始化Serial设备
            serial_config = self._load_serial_config()
            self.serial = AsyncSerialDevice(serial_config)
            logger.info("✅ 开始连接Serial...")

            # 同步等待Serial连接
            connect_event = threading.Event()
            connect_result = [False]

            async def _serial_connect_wrapper():
                connect_result[0] = await self.serial.connect()
                connect_event.set()

            self.tasker.add_task(_serial_connect_wrapper())
            if not connect_event.wait(timeout=5):
                raise TimeoutError("Serial连接超时（5秒未响应）")
            if not connect_result[0]:
                raise ConnectionError("Serial连接失败（返回False）")
            logger.info("✅ Serial已连接")

            # 3. 初始化Commander
            self.commander = Commander(self.code_config_path)
            self.commander.connect()
            logger.info("✅ Commander已初始化")

            # 4. 加载存储配置
            self._load_save_config()

            # 5. 连接管道（与C++通信）
            if not self.pipe.connect():
                raise ConnectionError("管道连接失败，无法与C++通信")

            self.connected = True
            logger.info("✅ ModbusClient所有组件初始化完成，设备已就绪")
            # 向C++发送初始化完成通知
            self.pipe.send({
                "type": "ready",
                "msg": "Modbus设备已初始化完成",
                "connected": True
            })

        except Exception as e:
            logger.error(f"❌ ModbusClient初始化失败: {str(e)}", exc_info=True)
            self.disconnect()
            # 向C++发送初始化失败通知（如果管道已连接）
            if self.pipe.running:
                self.pipe.send({
                    "type": "error",
                    "msg": f"初始化失败: {str(e)}",
                    "connected": False
                })

    def disconnect(self):
        """断开设备连接并清理资源"""
        logger.info("🔄 开始清理ModbusClient资源...")

        # 1. 关闭管道通信
        self.pipe.close()

        # 2. 清理Tasker
        if self.tasker:
            self.tasker.stop()
            logger.info("✅ Tasker已停止")

        # 3. 关闭Serial端口
        if self.serial and hasattr(self.serial, 'close'):
            self.serial.close()
            logger.info("✅ Serial端口已关闭")

        self.connected = False
        logger.info("🔚 ModbusClient资源清理完成")

    # ------------------------------
    # 原有设备控制逻辑（保持不变）
    # ------------------------------
    def _process_single_command(self, cmd: dict):
        try:
            control_type = cmd.get("control_type")
            data = cmd.get("data", {})
            if not control_type or not data:
                logger.warning(f"⚠️ 无效指令格式: {cmd}")
                return

            name = data.get("name")
            action = data.get("action")
            if not name or not action:
                logger.warning(f"⚠️ 指令缺少参数: {data}")
                return

            # 根据control_type获取指令
            if control_type == "key":
                hex_str = self.commander.key_message_dict[(name, action)]
            elif control_type == "flash":
                hex_str = self.commander.flash_message_dict[(name, action)]
            elif control_type == "linux":
                hex_str = self.commander.linux_message_dict[(name, action)]
            elif control_type == "discrete":
                hex_str = self.commander.discrete_message_dict[(name, action)]
            elif control_type == "power":
                hex_str = self.commander.power_message_dict[(name, action)]
            else:
                raise Exception(f"不支持的ControlType: {control_type}")

            self.tasker.add_task(self.serial.send_hex(hex_str))
            logger.info(f"✅ 已添加指令任务: {control_type} - {name}:{action}")

        except KeyError as e:
            logger.error(f"❌ 指令字典未找到匹配项: {(name, action)}（{control_type}）", exc_info=True)
            raise
        except Exception as e:
            logger.error(f"❌ 指令处理失败: {str(e)}（指令: {cmd}）", exc_info=True)
            raise

    async def _receive_device_power_status(self, msg_dict, name_key, action_key, idx):
        try:
            hex_cmd = msg_dict[(name_key, action_key)]
        except KeyError:
            logger.error(f"❌ 指令字典未找到键: ({name_key}, {action_key})")
            return 0

        success, rep = await self.serial.send_hex(hex_cmd)
        if not success or not rep:
            logger.warning(f"⚠️ 发送指令失败: ({name_key}, {action_key})")
            return 0

        try:
            bytes_list = [int(rep[i:i + 2], 16) for i in range(0, len(rep), 2)]
        except ValueError as e:
            logger.error(f"❌ 响应解析失败（{name_key}）: {str(e)}")
            return 0

        if len(bytes_list) <= idx:
            logger.warning(f"⚠️ 状态字节索引越界（{name_key}）: 长度{len(bytes_list)} < 索引{idx}")
            return 0

        return bytes_list[idx]

    async def _send_message_async(self, msg_dict, name, action, status_idx=3, target_bit=0):
        final_simple_list = []
        try:
            if not (0 <= target_bit <= 7):
                logger.error(f"❌ 目标bit非法：{target_bit}（仅支持0-7）")
                final_simple_list.append({"name": name, "status": False, "error": "无效bit位"})
                return final_simple_list

            if name == "discrete":
                # 处理多设备状态
                for (signal_name, signal_action), _ in msg_dict.items():
                    status_byte = await self._receive_device_power_status(
                        msg_dict=msg_dict,
                        name_key=signal_name,
                        action_key=signal_action,
                        idx=status_idx
                    )
                    final_simple_list.append({
                        "name": signal_name,
                        "status": bool((status_byte >> target_bit) & 1),
                        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
                    })
                logger.info(f"✅ discrete设备状态查询完成，生成{len(final_simple_list)}条记录")
            else:
                # 处理单设备状态
                status_byte = await self._receive_device_power_status(
                    msg_dict=msg_dict,
                    name_key=name,
                    action_key=action,
                    idx=status_idx
                )
                final_simple_list.append({
                    "name": name,
                    "status": bool((status_byte >> target_bit) & 1) if status_byte >= 0 else False,
                    "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
                })
                logger.info(f"✅ 单设备状态查询完成：{name}-{action}")

        except Exception as e:
            error_msg = str(e)[:50]
            final_simple_list.append({
                "name": name,
                "status": False,
                "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
                "error": error_msg
            })
            logger.error(f"❌ 设备状态查询失败: {error_msg}", exc_info=True)

        return final_simple_list

    def _send_message(self, msg_dict, name, action):
        self.tasker.add_task(self._send_message_async(msg_dict, name, action))
        logger.info(f"📥 已提交设备状态查询任务：{name}-{action}")


# 测试入口
if __name__ == "__main__":
    def main():
        try:
            client = ModbusClient()
            logger.info("开始初始化Modbus设备...")
            client.connect()

            if not client.connected:
                logger.error("设备初始化失败，退出")
                return 1

            # 保持运行，等待管道消息
            logger.info("设备初始化成功，等待C++指令...")
            while client.connected and client.pipe.running:
                time.sleep(1)

        except KeyboardInterrupt:
            logger.info("用户中断程序")
        except Exception as e:
            logger.error(f"运行异常: {str(e)}", exc_info=True)
            return 1
        finally:
            if 'client' in locals():
                client.disconnect()

        logger.info("程序退出")
        return 0

    sys.exit(main())